local skynet = require "skynet"

local s = require "service"
local table_utils = require "table_utils"
local scene_player_factory = require "scene_player"
local scene_map = require "scene_map"
local aoi = require "aoi"
local game_cmd = require "cmd_def"
local common_def = require "common_def"
local skill = require "skill"


s.uid2player = {}

function s.broadcast_aoi(x, y, msg, cmd_id)
    local result = aoi.query_range(x, y)
    skynet.error("broadcast_aoi", #result)
    for _, aoi_obj in pairs(result) do
        local player_id = aoi_obj.player_id
        local scene_player = s.uid2player[player_id]
        -- s.send(scene_player.node, scene_player.agent, "send", msg, cmd_id)
        -- 单个节点， TODO: 多个节点
        skynet.error("broadcast_aoi", scene_player:get_uid(), cmd_id)
        skynet.send(scene_player:get_gate(), "lua", "send", scene_player:get_uid(), cmd_id, msg)
    end
end

s.resp.request_enter = function(source, player_gate, player)
    skynet.error("request enter scene, player:" .. table_utils.table2string(player))
    if s.uid2player[player.uid] then
        return false
    end

    local scene_player = scene_player_factory.new(player)
    scene_player:set_gate(player_gate)
    s.uid2player[scene_player:get_uid()] = scene_player
    skynet.error("request enter scene, scene_player:" .. table_utils.table2string(scene_player))

    local player_scene_info = scene_player:get_scene_info()
    local position = player_scene_info.position
    local x, y, direction = position.x, position.y, position.direction
    if position.x == 0 and position.y == 0 then
        -- TODO: 场景配置默认坐标
        position.x, position.y = scene_map.born_x, scene_map.born_y
    end

    scene_player:set_position(position)
    scene_player:set_scene_id(s.id)
    scene_player:set_scene_gid(s.id)
    scene_player:set_speed()
    player_scene_info.scene_node = skynet.self()

    skynet.error("player_scene_info:" .. table_utils.table2string(player_scene_info))

    local aoi_obj = aoi.create_aoi_obj(scene_player:get_uid(), position.x, position.y)
    scene_player:set_aoi_obj(aoi_obj)
    scene_map.add_player_obj(scene_player:get_uid(), position.x, position.y)
    scene_map.test_print_map()

    local response = {
        error_code = game_cmd.ERROR_CODE.SUCCESS,
        scene_info = scene_player:get_scene_info()
    }
    s.broadcast_aoi(position.x, position.y, response, game_cmd.SERVER_CMD.ENTER_DEFAULT_SCENE_RESPONSE)

    return true, player_scene_info
end

s.resp.request_leave = function(source, uid)
    local scene_player = s.uid2player[uid]
    if not scene_player then
        skynet.error(uid .. "没有在场景" .. s.id)
        return false
    end

    -- 删除aoi玩家对象
    aoi.remove_aoi_obj(scene_player:get_aoi_obj())
    -- 删除map玩家对象
    scene_map.remove_player_obj(uid)
    s.uid2player[uid] = nil
    skynet.error(uid .. "退出场景" .. s.id)

    return true
end

s.resp.request_move = function(source, uid, msg)
    skynet.error("scene request move, uid:" .. uid)
    local scene_player = s.uid2player[uid]
    if not scene_player then
        skynet.error(uid .. "没有在场景" .. s.id)
        return false
    end

    local direction = msg.direction
    direction = math.floor(direction)
    if direction <= common_def.Direction.START or direction >= common_def.Direction.END then
        skynet.error("direction error:" .. direction .. " uid:" .. s.id)
        return false
    end

    skynet.error("position:" .. table_utils.table2string(scene_player:get_position()))
    if msg.stop_move then
        scene_player:remove_auto_moving()
        return
    end
    scene_player:set_direction(direction)
    
    if msg.auto_move then
        scene_player:set_auto_moving()
    else
        scene_player:remove_auto_moving()
    end
end

s.resp.request_use_skill = function(source, uid, msg)
    skynet.error("scene request use skill, uid:" .. uid)
    local scene_player = s.uid2player[uid]
    if not scene_player then
        skynet.error(uid .. "没有在场景" .. s.id)
        return false
    end

    skill.dispatch_skill(scene_player, msg.skill_id)
end

function move_update()
    for i, player in pairs(s.uid2player) do
        local position = player:get_position()
        if player:is_auto_moving() then
            local speed_x, speed_y = player:get_speed()
            if position.direction == common_def.Direction.UP then
                if speed_y <= 0 then
                    player:remove_auto_moving()
                end
                position.y = position.y - speed_y * 0.1
            elseif position.direction == common_def.Direction.DOWN then
                if speed_y <= 0 then
                    player:remove_auto_moving()
                end
                position.y = position.y + speed_y * 0.1
            elseif position.direction == common_def.Direction.LEFT then
                if speed_x <= 0 then
                    player:remove_auto_moving()
                end
                position.x = position.x - speed_x * 0.1
            elseif position.direction == common_def.Direction.RIGHT then
                if speed_x <= 0 then
                    player:remove_auto_moving()
                end
                position.x = position.x + speed_x * 0.1
            end

            if position.x > scene_map.max_x then
                position.x = scene_map.max_x
                player:remove_auto_moving()
                -- s.resp.stop_move(_, b.playerid)
            end
            if position.y > scene_map.max_y then
                position.y = scene_map.max_y
                player:remove_auto_moving()
                -- s.resp.stop_move(_, b.playerid)
            end

            if position.x < 0 then
                position.x = 0
                player:remove_auto_moving()
                -- s.resp.stop_move(_, b.playerid)
            end
            if position.y < 0 then
                position.y = 0
                player:remove_auto_moving()
                -- s.resp.stop_move(_, b.playerid)
            end

            local response = {
                scene_info = player:get_scene_info(),
                uid = player:get_uid()
            }
            scene_map.update_player_obj(player:get_uid(), position.x, position.y)
            scene_map.test_print_map()
            aoi.update_aoi_obj(player:get_aoi_obj(), position.x, position.y)
            s.broadcast_aoi(position.x, position.y, response, game_cmd.SERVER_CMD.MOVE)
        end
    end
end

function update(frame)
    -- food_update()
    move_update()
    -- eat_update()
    -- 碰撞略
    -- 分裂略
end

s.init = function()
    scene_map.init_map_by_id(s.id)
    scene_map.test_print_map()

    skynet.fork(function()
        -- 保持帧率执行
        local stime = skynet.now()
        local frame = 0
        while true do
            frame = frame + 1
            local isok, err = pcall(update, frame)
            if not isok then
                skynet.error(err)
            end
            local etime = skynet.now()
            local waittime = frame * 20 - (etime - stime)
            if waittime <= 0 then
                waittime = 2
            end
            skynet.sleep(waittime)
        end
    end)
end

s.start(...)